4장. 변수와 자료형
프로그램은 결국 “값을 다루는 일” 이다. 값을 담아 둘 그릇이 변수고, 그 그릇이 어떤 모양인지를 정하는 게 자료형이다.
이 장에서 Go 의 변수 선언법과 기본 자료형을 익힌다.
목표:
var와:=의 차이를 설명할 수 있게 되기- Go 의 기본 자료형 종류를 알기
- 타입 변환은 항상 명시적이어야 한다는 점 이해하기
4.1 변수 선언
var 키워드
가장 기본은 var 키워드다.
세 가지 형태를 모두 지원한다.
var x int // 타입만 지정 (값은 제로값)
var y int = 10 // 타입과 값 모두 지정
var z = 10 // 타입은 추론 (값으로부터)
var z = 10 처럼 타입을 생략하면
컴파일러가 우변의 값으로부터 타입을 추론한다.
이 경우 z 는 자동으로 int 가 된다.
:= 짧은 선언
함수 안에서는 := 로 더 짧게 쓸 수 있다.
x := 10
name := "Go"
var x = 10과 동일한 의미다var와 타입 둘 다 생략된다- 단, 함수 바깥(패키지 수준)에서는 못 쓴다
둘의 차이와 언제 쓰는가
| 구분 | var | := |
|---|---|---|
| 사용 가능 위치 | 어디서나 | 함수 안에서만 |
| 타입 명시 | 가능 | 불가 (항상 추론) |
| 초기값 없이 선언 | 가능 (var x int) | 불가 |
보통 함수 안에서는 := 를 쓰고,
패키지 수준 변수에는 var 를 쓴다.
초기값 없이 그릇만 만들고 싶을 때도 var 가 필요하다.
여러 변수 한 번에 선언
같은 타입이라면 콤마로 묶을 수 있다.
var a, b int // 둘 다 int, 값은 제로값
var x, y int = 1, 2 // 둘 다 int, 각각 1과 2
a, b := 10, 20 // 짧은 선언으로도 가능
심지어 타입이 달라도 된다.
name, age := "Alice", 30
이 기능은 함수가 여러 값을 반환할 때 유용하게 쓰인다. (다중 반환은 9장에서 다룬다.)
var 블록 선언
여러 변수를 묶어서 깔끔하게 선언할 때 쓴다.
var (
name string
age int
isAdmin bool
)
가독성도 좋고, 패키지 수준 변수를 모아 둘 때 자주 쓰는 패턴이다.
값까지 함께 줄 수도 있다.
var (
name = "Alice"
age = 30
isAdmin = false
)
4.2 기본 자료형
Go 의 기본 자료형은 크게 네 그룹이다.
- 정수 (integer)
- 실수 (float)
- 불리언 (bool)
- 문자열 (string)
정수
이름에 비트 수가 그대로 붙는다.
| 타입 | 크기 | 범위 |
|---|---|---|
int8 | 1바이트 | -128 ~ 127 |
int16 | 2바이트 | -32,768 ~ 32,767 |
int32 | 4바이트 | 약 ±21억 |
int64 | 8바이트 | 약 ±9.2 × 10¹⁸ |
uint8 | 1바이트 | 0 ~ 255 |
uint16 | 2바이트 | 0 ~ 65,535 |
uint32 | 4바이트 | 0 ~ 약 42억 |
uint64 | 8바이트 | 0 ~ 약 1.8 × 10¹⁹ |
u 가 붙은 쪽은 unsigned, 즉 음수가 없다.
int 와 uint 는 별도다.
- 플랫폼에 따라 32비트 또는 64비트가 된다
- 요즘 대부분의 환경에서는 64비트
- 특별한 이유가 없다면 그냥
int를 쓰면 된다
byte 와 rune
이 두 타입은 사실 정수의 별명(alias) 이다.
byte=uint8rune=int32
byte 는 “한 바이트” 라는 의미를 강조할 때,
rune 은 “한 글자(유니코드)” 라는 의미를 강조할 때 쓴다.
자세한 내용은 6장에서 다룬다.
실수
| 타입 | 크기 | 비고 |
|---|---|---|
float32 | 4바이트 | 정밀도 약 7자리 |
float64 | 8바이트 | 정밀도 약 15자리 |
특별한 이유가 없다면 float64 를 쓴다.
var pi float64 = 3.14159
e := 2.71828 // 실수 리터럴은 기본 float64
불리언
참(true) / 거짓(false) 두 값만 가진다.
var ok bool = true
done := false
C 와 달리 정수 0/1 과 자동으로 교환되지 않는다.
조건문에는 반드시 bool 만 들어간다.
문자열
문자열은 string 타입이다.
var greeting string = "Hello"
name := "Go"
큰따옴표로 감싸야 하며, 작은따옴표는 다른 의미다 (단일 문자 = rune). 자세한 내용은 6장에서 다룬다.
빠른 요약
| 그룹 | 대표 타입 | 예시 값 |
|---|---|---|
| 정수 | int | 42, -7 |
| 실수 | float64 | 3.14, -0.5 |
| 불리언 | bool | true, false |
| 문자열 | string | "Hello" |
4.3 상수
값이 한 번 정해지면 절대 바뀌지 않는 것은 상수(constant) 다.
const 키워드로 선언한다.
const Pi = 3.14159
const Greeting = "Hello"
- 컴파일 시점에 값이 고정된다
- 실행 중에 바꿀 수 없다
- 함수 호출 결과처럼 런타임에야 정해지는 값은 못 쓴다
타입을 지정할 수도 있다
const MaxUsers int = 100
지정하지 않으면 “타입 없는 상수” 로 남는다. 필요한 곳에서 자동으로 알맞은 타입으로 바뀌어 유연하게 동작한다.
const 블록
var 와 마찬가지로 블록으로 묶을 수 있다.
const (
Pi = 3.14159
Greeting = "Hello"
MaxUsers = 100
)
iota 맛보기
연속된 상수를 깔끔하게 정의하는 방법이 있다.
iota 라는 특별한 식별자다.
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota는const블록 안에서 0부터 시작- 한 줄 내려갈 때마다 1씩 증가
- 다른 언어의 enum (열거형) 흉내를 낼 때 자주 쓴다
지금은 “이런 게 있다” 정도만 알아 두면 된다.
4.4 타입 변환
Go 는 다른 언어와 비교해 타입 변환에 매우 엄격하다.
묵시적 변환이 전혀 없다. 다른 타입끼리 섞으려면 반드시 직접 변환해야 한다.
명시적 변환
타입(값) 형태로 변환한다.
var i int = 10
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
이걸 빠뜨리면 컴파일 에러가 난다.
var i int = 10
var f float64 = i // 에러: cannot use i (int) as float64
같은 정수 계열이라도 그렇다.
var a int32 = 10
var b int64 = a // 에러: int32 와 int64 는 다른 타입
처음엔 번거롭게 느껴지지만, “의도하지 않은 변환으로 인한 버그” 가 원천 차단된다.
정수 ↔ 실수 변환
실수를 정수로 바꾸면 소수점 아래는 잘려 나간다. 반올림이 아니라 그냥 버린다.
var f float64 = 3.9
var i int = int(f) // i 는 3 (4가 아님)
음수도 마찬가지로 0 쪽으로 잘린다.
var f float64 = -3.9
var i int = int(f) // i 는 -3
string(z) — 헷갈리는 케이스
정수에서 문자열로 변환할 때 특히 주의해야 한다.
var n int = 65
var s string = string(n)
s 는 무엇이 될까?
직관적으로는 "65" 가 될 것 같지만,
실제로는 "A" 가 된다.
Go 는
string(정수)를 “그 정수를 유니코드 코드 포인트로 해석” 한다. 65 는 알파벳A의 유니코드 값이다.
숫자를 문자열로 바꾸고 싶을 땐 다른 도구를 써야 한다.
import "strconv"
n := 65
s := strconv.Itoa(n) // s 는 "65"
strconv 패키지는 27장에서 자세히 다룬다.
지금은 “string(숫자) 는 위험하다” 정도만 기억하자.
4.5 제로값 (zero value)
Go 에서는 변수를 선언만 하고 값을 주지 않아도 “쓰레기 값” 이 들어가지 않는다.
타입별로 정해진 기본값이 자동으로 들어간다. 이걸 제로값 이라고 부른다.
타입별 제로값
| 타입 | 제로값 |
|---|---|
정수 (int, int64 등) | 0 |
실수 (float32, float64) | 0.0 |
불리언 (bool) | false |
문자열 (string) | "" (빈 문자열) |
| 포인터, 인터페이스, 함수, 채널, 맵, 슬라이스 | nil |
var n int // n 은 0
var f float64 // f 는 0.0
var ok bool // ok 는 false
var s string // s 는 ""
C/C++ 의 쓰레기값 문제가 없다
C 나 C++ 에서는 지역 변수를 초기화 없이 선언하면 이전에 그 메모리에 남아 있던 값이 그대로 보인다. “운 좋으면 0, 운 나쁘면 이상한 숫자” 라 디버깅하기 어려운 버그의 단골 원인이었다.
Go 는 이 문제를 언어 차원에서 막는다. 선언만 했다면 항상 동일한 제로값이 들어 있다.
var count int
count++ // 안전. count 는 1
처음 만났을 때 어색할 수 있지만, 하나만 기억하면 된다.
선언했다면 이미 값이 있다. 그 값은 그 타입의 “비어 있는 상태” 다.
4.6 정리
- 변수 선언은
var또는:=로 한다:=는 함수 안에서만 쓸 수 있다
- 기본 자료형은 정수 / 실수 / 불리언 / 문자열 네 그룹
- 특별한 이유가 없으면
int,float64를 쓴다
- 특별한 이유가 없으면
- 상수는
const로 선언, 컴파일 시점에 고정된다 - 타입 변환은 항상 명시적이다 (
int(x),float64(y))string(숫자)는 유니코드 코드 포인트로 해석되니 주의
- 초기화하지 않은 변수는 자동으로 제로값을 가진다
자료형이 갖춰졌으면 그 값들을 가지고 계산을 해 봐야 한다. 다음 장에서는 산술, 비교, 논리 같은 연산자들을 다룬다.